package com.usefullc.user.service;

import cn.hutool.core.util.BooleanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.usefullc.common.constants.CacheConstants;
import com.usefullc.common.constants.DataConfigConstant;
import com.usefullc.common.constants.SystemConstant;
import com.usefullc.common.constants.UserConstants;
import com.usefullc.common.enums.CheckCodeTypeEnum;
import com.usefullc.common.enums.YesNoEnum;
import com.usefullc.common.exception.BizException;
import com.usefullc.common.user.OnlineUser;
import com.usefullc.common.user.OnlineUserManager;
import com.usefullc.common.util.*;
import com.usefullc.common.util.http.HttpNewUtils;
import com.usefullc.common.util.http.ResponseRes;
import com.usefullc.system.entity.Pager;
import com.usefullc.system.service.AbstractBizService;
import com.usefullc.system.service.DataConfigService;
import com.usefullc.system.service.SerialNumberService;
import com.usefullc.system.service.component.SystemConfigComponent;
import com.usefullc.system.validator.UsefulPropertyVerify;
import com.usefullc.user.domain.*;
import com.usefullc.user.enums.*;
import com.usefullc.user.mapper.UserMapper;
import com.usefullc.user.param.*;
import com.usefullc.user.query.RoleQuery;
import com.usefullc.user.query.TenantUserQuery;
import com.usefullc.user.query.UserQuery;
import com.usefullc.user.vo.LoginResultVo;
import com.usefullc.user.vo.UserVo;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 用户服务类
 *
 * @author: Sampson
 * @date: 2018-12-19 16:05
 */
@Service
public class UserService extends AbstractBizService<UserMapper, User, UserQuery> {


    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @Autowired
    private OuterUserService outerUserService;

    @Autowired
    private EmployeeService employeeService;


    @Autowired
    private SystemConfigComponent systemConfigComponent;

    @Autowired
    private SerialNumberService serialNumberService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private TenantUserService tenantUserService;

    @Autowired
    private TenantService tenantService;

    @Autowired
    private DataConfigService dataConfigService;

    @Value("${usefullc.api.host}")
    private String apiHost;


    @Override
    protected QueryWrapper<User> getQueryWrapper(UserQuery query) {
        QueryWrapper<User> queryWrapper = super.getQueryWrapper(query);
        OnlineUser loginUser = OnlineUserManager.getLoginUser();
        if (loginUser != null && loginUser.getTenantId() != 0L) {
            query.setTenantId(loginUser.getTenantId());
//            if(loginUser.getTenantId() == 0L){
//                query.setTenantIdStart(0L);
//            }
        }

//        if (StringUtils.isNotEmpty(thisQuery.getBirthdayStart())) {
//            Expression<String> endTimeExpression = criteriaBuilder.function("DATE_FORMAT", String.class,
//                    root.get("birthday"), criteriaBuilder.literal("%m-%d"));
//            predicates.add(criteriaBuilder.greaterThanOrEqualTo(endTimeExpression, thisQuery.getBirthdayStart()));
//        }
//        if (StringUtils.isNotEmpty(thisQuery.getBirthdayEnd())) {
//            Expression<String> endTimeExpression = criteriaBuilder.function("DATE_FORMAT", String.class,
//                    root.get("birthday"), criteriaBuilder.literal("%m-%d"));
//            predicates.add(criteriaBuilder.lessThanOrEqualTo(endTimeExpression, thisQuery.getBirthdayEnd()));
//        }
//        if (StringUtils.isNotEmpty(thisQuery.getGmtCreateStart())) {
//            Expression<String> endTimeExpression = criteriaBuilder.function("DATE_FORMAT", String.class,
//                    root.get("gmtCreate"), criteriaBuilder.literal("%Y-%m-%d"));
//            predicates.add(criteriaBuilder.greaterThanOrEqualTo(endTimeExpression, thisQuery.getGmtCreateStart()));
//        }
//        if (StringUtils.isNotEmpty(thisQuery.getGmtCreateEnd())) {
//            Expression<String> endTimeExpression = criteriaBuilder.function("DATE_FORMAT", String.class,
//                    root.get("gmtCreate"), criteriaBuilder.literal("%Y-%m-%d"));
//            predicates.add(criteriaBuilder.lessThanOrEqualTo(endTimeExpression, thisQuery.getGmtCreateEnd()));
//        }
        return queryWrapper;
    }

    public Pager<UserVo> findVoByPage(UserQuery query) {
        Pager<User> pager = this.findByPage(query);
        Pager<UserVo> newPager = new Pager<>(pager);
        List<User> dataList = pager.getDataList();
        List<UserVo> newDataList = new ArrayList<>();
        newPager.setDataList(newDataList);
        for (User user : dataList) {
            UserVo userVo = BeanUtils.beanCopy(user, UserVo.class);
            List<Role> roleList = this.getBaseMapper().findRoleListByUserId(user.getId());
            if (CollectionUtils.isNotEmpty(roleList)) {
                List<Long> roleIds = roleList.stream().map(data -> data.getId()).collect(Collectors.toList());
                userVo.setRoleIds(roleIds);
            }
            if(TenantUtils.isValidateTenantId(userVo.getTenantId())){
                Tenant tenant = tenantService.findById(user.getTenantId());
                if(tenant != null){
                    userVo.setTenantName(tenant.getName());
                }
            }
            newDataList.add(userVo);

            //获取员工
//            EmployeeQuery employeeQuery = new EmployeeQuery();
//            employeeQuery.setUserId(user.getId());
//            Employee employee = employeeService.findOne(employeeQuery);
//            userVo.setEmployee(employee);


        }

        return newPager;
    }


    public User findByMobile( String areaCode, String mobile) {
        UserQuery userQuery = new UserQuery();
//        userQuery.setType(type);
        userQuery.setAreaCode(areaCode);
        userQuery.setMobile(mobile);
        return this.findOne(userQuery);
    }

    public User findByEmail( String email) {

        UserQuery userQuery = new UserQuery();
//        userQuery.setType(type);
        userQuery.setEmail(email);
        return this.findOne(userQuery);

    }

    public User findByMobile( String mobile) {
        UserQuery userQuery = new UserQuery();
//        userQuery.setType(type);
        userQuery.setMobile(mobile);
        return this.findOne(userQuery);
    }

    public User findByAccountNo( String accountNo) {
        UserQuery userQuery = new UserQuery();
//        userQuery.setType(type);
        userQuery.setAccountNo(accountNo);
        return this.findOne(userQuery);

    }


    public boolean checkUserUniqueByAccountNo(String accountNo) {
        User user = this.findByAccountNo( accountNo);
        return user == null;
    }

    public boolean checkUserUniqueByMobile( String mobile) {
        User user = this.findByMobile( "86", mobile);
        return user == null;
    }

    @Transactional
    public User saveUser(User user) {
        //密码加密
        String newPwd = MD5Utils.MD5EncodeForPwd(UserConstants.PWD_KEY, user.getPwd());
        user.setPwd(newPwd);

        if (StringUtils.isEmpty(user.getNickname())) {
            user.setNickname(user.getAccountNo());
        }
        this.save(user);

//        if (user.getType() == UserTypeEnum.MEMBER.getCode()) {  //去掉，
            //account
//            UserAccount userAccount = new UserAccount();
//            userAccount.setEnable(YesNoEnum.YES.getCode());
//            userAccount.setUserId(savedUser.getId());
//            userAccount.setAmount(0L);
//            userAccount.setWithdrawAmount(0L);
//            userAccountService.save(userAccount);

//        }
        return user;
    }

    public User register(UserRegisterParam userRegisterParam) {
        Integer registerType = userRegisterParam.getRegisterType();
//        Integer userType = userRegisterParam.getUserType();
        String areaCode = userRegisterParam.getAreaCode();
        String mobile = userRegisterParam.getMobile();
        String email = userRegisterParam.getEmail();
        String checkCode = userRegisterParam.getCheckCode();
        String pwd = userRegisterParam.getPwd();
        //手机号码是否已被注册
//        String accountNo = areaCode + mobile;
        String accountNo =  mobile;

        User dbUser = null;
        if (registerType == RegisterTypeEnum.MOBILE.getCode()) {
            dbUser = this.findByMobile( areaCode, mobile);
        } else {
            dbUser = this.findByEmail( email);
            accountNo = email;
        }
        if (dbUser != null) {
            throw new BizException("手机号码已被注册",false);
        }

        //短信验证码是否正确
        String checkCodeKey = StringUtils.join(new Object[]{CacheConstants.USER_REGISTER_SMS, areaCode+accountNo}, "_");
//        String checkCodeKey = this.getCacheKey(CacheConstants.USER_REGISTER_SMS, userType);
        logger.info("checkCodeKey={}",checkCodeKey);

        String cacheSmsCode = stringRedisTemplate.opsForValue().get(checkCodeKey);
        if (StringUtils.isEmpty(cacheSmsCode)) {
            throw new BizException("验证码已失效，请重新获取",false);
        }

        if (!StringUtils.equals(cacheSmsCode, checkCode)) {
            throw new BizException("验证码错误，请重新输入",false);
        }

        //密码加密
        String newPwd = MD5Utils.MD5EncodeForPwd(UserConstants.PWD_KEY, pwd.toUpperCase());

        //保存
        User user = new User();
        if (StringUtils.isNotEmpty(userRegisterParam.getAccountNo())) {
            accountNo = userRegisterParam.getAccountNo();
        }
        user.setAccountNo(accountNo);  //默认账号，可以修改，必须保证唯一性
//        user.setType(userType);
        user.setAreaCode(areaCode);
        user.setMobile(mobile);
        user.setEmail(email);
        user.setNickname(accountNo);
        user.setPwd(newPwd);
        user.setGender(UserGenderEnum.MALE.getCode());
        user.setStatus(UserStatusEnum.PASS.getCode());
        user.setUserName(accountNo);
        user.setBind(YesNoEnum.NO.getCode());
        user.setAvatar(apiHost + "/system/common/res/system/person.png");  //default
        //是否默认绑定租户
        String autoBindTenant = dataConfigService.findValueByCode(DataConfigConstant.AUTO_BIND_TENANT);
        if (BooleanUtil.toBoolean(autoBindTenant)) {
            String autoBindTenantId = dataConfigService.findValueByCode(DataConfigConstant.AUTO_BIND_TENANT_ID);
            user.setTenantId(NumberUtil.getNumber(autoBindTenantId));
            user.setBindTenant(YesNoEnum.YES.getCode());
        }else{
            user.setTenantId(-1L);  //表示未知
            user.setBindTenant(YesNoEnum.NO.getCode());
        }

        UserEditParam userEditParam = new UserEditParam();
        List<Long> roleIds = new ArrayList<>(); //后续放到配置表中
        roleIds.add(2L);
        userEditParam.setRoleIds(roleIds);
        this.saveUser(user,userEditParam);
        return user;
    }


    public User registerByAccount(UserRegisterParam userRegisterParam) {
//        Integer registerType = userRegisterParam.getRegisterType();
//        Integer userType = userRegisterParam.getUserType();
        String areaCode = userRegisterParam.getAreaCode();
        String mobile = userRegisterParam.getMobile();
        String email = userRegisterParam.getEmail();
        String checkCode = userRegisterParam.getCheckCode();
        String pwd = userRegisterParam.getPwd();
        String accountNo = userRegisterParam.getAccountNo();
        String deviceId = userRegisterParam.getDeviceId();

        //账号是否已被注册
        User dbUser = this.findByAccountNo( accountNo);
        if (dbUser != null) {
            throw new BizException("该账号已被注册");
        }

        //短信验证码是否正确
        String checkCodeKey = StringUtils.join(new Object[]{CacheConstants.USER_REGISTER_SMS, deviceId}, "_");
//        String checkCodeKey = this.getCacheKey(CacheConstants.USER_REGISTER_SMS, userType);

        String cacheSmsCode = stringRedisTemplate.opsForValue().get(checkCodeKey);
        if (StringUtils.isEmpty(cacheSmsCode)) {
            throw new BizException("验证码已失效，请重新获取");
        }

        if (!StringUtils.equals(cacheSmsCode, checkCode)) {
            throw new BizException("验证码错误，请重新输入");
        }

        //密码加密
        String newPwd = MD5Utils.MD5EncodeForPwd(UserConstants.PWD_KEY, pwd.toUpperCase());

        //保存
        User user = new User();
        if (StringUtils.isNotEmpty(userRegisterParam.getAccountNo())) {
            accountNo = userRegisterParam.getAccountNo();
        }
        user.setAccountNo(accountNo);  //默认账号，可以修改，必须保证唯一性
//        user.setType(userType);
        user.setAreaCode(areaCode);
        user.setMobile(mobile);
        user.setEmail(email);
        user.setNickname(accountNo);
        user.setPwd(newPwd);
        user.setGender(UserGenderEnum.MALE.getCode());
        user.setStatus(UserStatusEnum.INIT.getCode());  //等待审核
        user.setUserName(accountNo);
        user.setBind(YesNoEnum.NO.getCode());
        user.setAvatar(apiHost + "/system/common/res/system/person.png");  //默认图片 TODO

        //默认角色

        this.save(user);
        return user;
    }


    public void saveUser(User user, UserEditParam param) {
//        user.setType(UserTypeEnum.ADMIN.getCode());

//        user.setStatus(UserStatusEnum.PASS.getCode());
        if (user.getId() != null) {
            this.update(user);
        } else {
            this.save(user);
        }

        //先删除角色
        this.getBaseMapper().deleteUserRoleByUserId(user.getId());

        List<Long> roleIds = param.getRoleIds();
        if (CollectionUtils.isNotEmpty(roleIds)) {
            List<UserRoleParam> userRoleParams = new ArrayList<>();
            for (Long roleId : roleIds) {
                UserRoleParam userRoleParam = new UserRoleParam();
                userRoleParam.setRoleId(roleId);
                userRoleParam.setUserId(user.getId());
                userRoleParams.add(userRoleParam);
            }
            this.getBaseMapper().insertUserRoleBatch(userRoleParams);
        }
    }


    //只删除用户信息，角色其他的信息不处理
//    public void saveUserInfo(User user, UserEditParam param) {
//        user.setType(UserTypeEnum.ADMIN.getCode());
//
//        user.setStatus(UserStatusEnum.PASS.getCode());
//        if (user.getId() != null) {
//            this.update(user);
//        } else {
//            this.save(user);
//        }
//    }

    public UserVo getLoginUser(HttpServletRequest request) {
        String token = request.getHeader(SystemConstant.TOKEN);
        if (StringUtils.isEmpty(token)) {  //针对web 端，从cookie 读取
            token = CookieUtil.getCookieValue(request, SystemConstant.TOKEN);
        }
        if (StringUtils.isEmpty(token)) {
            return null;
        }
        String tokenCacheKey = getUserTokenKey(token);
//        String tokenCacheKey = StringUtils.join(new Object[]{CacheConstants.USER_TOKEN, token}, "_");
        try {
            Object cacheUser = redisTemplate.opsForValue().get(tokenCacheKey);
            if (cacheUser != null) {
                UserVo userVo = (UserVo) cacheUser;
                //重数据刷一遍数据库，拿到最新的
                User user = findById(userVo.getId());
                userVo = BeanUtils.beanCopy(user, userVo);
                return userVo;
            }

        } catch (Exception e) {
            logger.error("get login user error", e);
        }
        return null;
    }

    public int updateStatus(Long id, Integer status) {
        return this.getBaseMapper().updateStatus(id, status);
    }

    public int updateAllStatus(List<Long> ids, Integer status) {
        OnlineUser loginUser = OnlineUserManager.getLoginUser();
        int result = this.getBaseMapper().updateAllStatus(ids, status);
        if (result > 0) {
            UserQuery userQuery = new UserQuery();
            userQuery.setIds(ids);
            List<User> userList = this.findAll(userQuery);
            for (User user : userList) {
                if(TenantUtils.isValidateTenantId(user.getTenantId())){
//                    TenantUserQuery tenantUserQuery = new TenantUserQuery();
//                    tenantUserQuery.setUserId(user.getId());
//                    tenantUserQuery.setTenantId(user.getTenantId());
                    TenantUser tenantUser = tenantUserService.findByUserIdAndTenantId(user.getId(),user.getTenantId());
                    if(tenantUser == null){
                        tenantUser = new TenantUser();
                        tenantUser.setUserId(user.getId());
                        tenantUser.setTenantId(user.getTenantId());
                    }
                    tenantUser.setStatus(status);
                    tenantUser.setGmtAudit(new Date());
                    tenantUser.setAuditUserId(loginUser.getUserId());
                    tenantUserService.save(tenantUser);
                    user.setTenantId(tenantUser.getTenantId());
                    this.update(user);  //修改用户租户ID
                }
                this.removeUserCache(user.getId());  //最后清除在线缓存
            }
        }
        return result;
    }


    public void updatePwd(Long userId, String oldPwd, String newPwd) {
        User user = this.findById(userId);
        //密码加密
        String encodePwd = MD5Utils.MD5EncodeForPwd(UserConstants.PWD_KEY, oldPwd.toUpperCase());
        if (!StringUtils.equalsIgnoreCase(encodePwd, user.getPwd())) {
            throw new BizException("原始密码错误，请重新输入");
        }
        //密码加密
        String encodeNewPwd = MD5Utils.MD5EncodeForPwd(UserConstants.PWD_KEY, newPwd.toUpperCase());

        //修改
        user.setPwd(encodeNewPwd);
        this.save(user);
    }

    public String resetPwd(List<Long> userIds) {
        String pwd = RandomStringUtils.random(8, true, true);
        String md5Pwd = MD5Utils.MD5Encode(pwd);
        //密码加密
        String encodePwd = MD5Utils.MD5EncodeForPwd(UserConstants.PWD_KEY, md5Pwd);
        this.getBaseMapper().updateAllPwd(userIds, encodePwd);
        return pwd;
    }

    public void updateForgetLoginPwdBySmsCode( String areaCode, String mobile, String smsCode, String newPwd) {
        User user = this.findByMobile( areaCode, mobile);
        if (user == null) {
            throw new BizException("该用户没有注册");
        }
        //短信验证码是否正确
        String smsCodeKey = StringUtils.join(new Object[]{CacheConstants.USER_FORGET_PWD_SMS, areaCode + mobile}, "_");
//        String smsCodeKey = this.getCacheKey(CacheConstants.USER_FORGET_PWD_SMS,userType,areaCode+mobile);

        String cacheSmsCode = stringRedisTemplate.opsForValue().get(smsCodeKey);
        if (StringUtils.isEmpty(cacheSmsCode)) {
            throw new BizException("验证码已失效，请重新获取");
        }

        if (!StringUtils.equals(cacheSmsCode, smsCode)) {
            throw new BizException("验证码错误，请重新输入");
        }
        //密码加密
        String encodePwd = MD5Utils.MD5EncodeForPwd(UserConstants.PWD_KEY, newPwd);

        //修改
        user.setPwd(encodePwd);
        this.save(user);
    }

    public void updateForgetLoginPwdByEmailCode(String email, String smsCode, String newPwd) {
        User user = this.findByEmail( email);
        if (user == null) {
            throw new BizException("该用户没有注册");
        }
        //短信验证码是否正确
        String smsCodeKey = StringUtils.join(new Object[]{CacheConstants.USER_FORGET_PWD_SMS, email}, "_");

        String cacheSmsCode = stringRedisTemplate.opsForValue().get(smsCodeKey);
        if (StringUtils.isEmpty(cacheSmsCode)) {
            throw new BizException("验证码已失效，请重新获取");
        }

        if (!StringUtils.equals(cacheSmsCode, smsCode)) {
            throw new BizException("验证码错误，请重新输入");
        }
        //密码加密
        String encodePwd = MD5Utils.MD5EncodeForPwd(UserConstants.PWD_KEY, newPwd);

        //修改
        user.setPwd(encodePwd);
        this.save(user);
    }

    public void updateMobile(Long userId, String areaCode, String mobile, String smsCode) {
        User user = this.findById(userId);
        if (user == null) {
            throw new BizException("该用户没有注册");
        }
        //短信验证码是否正确
        String smsCodeKey = StringUtils.join(new Object[]{CacheConstants.USER_CHANGE_MOBILE_SMS, areaCode + mobile}, "_");

        String cacheSmsCode = stringRedisTemplate.opsForValue().get(smsCodeKey);
        if (StringUtils.isEmpty(cacheSmsCode)) {
            throw new BizException("验证码已失效，请重新获取");
        }

        if (!StringUtils.equals(cacheSmsCode, smsCode)) {
            throw new BizException("验证码错误，请重新输入");
        }

        //修改
        user.setMobile(mobile);
        this.save(user);
    }


    public void updateEmail(Long userId, String email, String smsCode) {
        User user = this.findById(userId);
        if (user == null) {
            throw new BizException("该用户没有注册");
        }
        //短信验证码是否正确
        String smsCodeKey = StringUtils.join(new Object[]{CacheConstants.USER_CHANGE_MOBILE_SMS, email}, "_");

        String cacheSmsCode = stringRedisTemplate.opsForValue().get(smsCodeKey);
        if (StringUtils.isEmpty(cacheSmsCode)) {
            throw new BizException("验证码已失效，请重新获取");
        }

        if (!StringUtils.equals(cacheSmsCode, smsCode)) {
            throw new BizException("验证码错误，请重新输入");
        }

        //修改
        user.setEmail(email);
        this.save(user);
    }

    private String getUserTokenKey(Object key) {
        String appEnv = systemConfigComponent.getAppEnv();
        String userIdCacheKey = StringUtils.join(new Object[]{CacheConstants.USER_TOKEN, appEnv, key}, "_");
        return userIdCacheKey;
    }

    private String getCacheKey(String prefix, Object[] args) {
        String appEnv = systemConfigComponent.getAppEnv();
        String userIdCacheKey = StringUtils.join(new Object[]{prefix, appEnv, args}, "_");
        return userIdCacheKey;
    }

    private void removeUserCache(Long userId) {
        String userIdCacheKey = getUserTokenKey(userId);
        String oldToken = stringRedisTemplate.opsForValue().get(userIdCacheKey);
        String oldTokenCacheKey = this.getUserTokenKey(oldToken);
        redisTemplate.delete(oldTokenCacheKey);
    }

    public String resetLoginToken(User user) {
        //剔除老的 token user 对象
        this.removeUserCache(user.getId());

        //set new token for user 对象
        String userIdCacheKey = getUserTokenKey(user.getId());
        String newToken = UUID.randomUUID().toString();
        stringRedisTemplate.opsForValue().set(userIdCacheKey, newToken);
        String tokenCacheKey = this.getUserTokenKey(newToken);
        UserVo userVo = BeanUtils.beanCopy(user, UserVo.class);
        List<Role> roleList = this.getBaseMapper().findRoleListByUserId(user.getId());
        if (CollectionUtils.isNotEmpty(roleList)) {
            List<Long> roleIds = roleList.stream().map(data -> data.getId()).collect(Collectors.toList());
            userVo.setRoleIds(roleIds);
        }
        redisTemplate.opsForValue().set(tokenCacheKey, userVo);

        //针对web端，放到cookie 中
        HttpServletResponse response = ResponseUtils.getResponse();
        if (response != null) {
//            Cookie sidCookie = new Cookie("sid",uuid);
//            sidCookie.setMaxAge(DateUtil.MONTH_SECONDS);
//            sidCookie.setPath("/");
//            response.addCookie(sidCookie);
            CookieUtil.setCookie(null, response, SystemConstant.TOKEN, newToken, (int) TimeUnit.DAYS.toSeconds(30L));
        }
        return newToken;

    }


    public void clearLoginUser(HttpServletRequest request) {
        String token = request.getHeader(SystemConstant.TOKEN);
        if (StringUtils.isEmpty(token)) {  //针对web 端，从cookie 读取
            token = CookieUtil.getCookieValue(request, SystemConstant.TOKEN);
        }
        if (StringUtils.isEmpty(token)) {
            return;
        }
//        String tokenCacheKey = StringUtils.join(new Object[]{CacheConstants.USER_TOKEN, token}, "_");
        String tokenCacheKey = this.getUserTokenKey(token);
        redisTemplate.delete(tokenCacheKey);
    }

//    public User loginByPwd(Integer userType, String accountNo, String pwd) {
//        User user = this.findByAccountNo(userType, accountNo);
//        if (user == null) {
//            throw new BizException("账号不存在");
//        }
//        String newPwd = MD5Utils.MD5EncodeForPwd(UserConstants.PWD_KEY, pwd);
//        if (!user.getPwd().equals(newPwd)) {
//            throw new BizException("密码不正确");
//        }
//        return user;
//    }

    public User loginByPwd(String accountNo, String pwd, String imgCode, String deviceId) {
        //图形验证码是否正确
        String checkCodeKey = StringUtils.join(new Object[]{CacheConstants.USER_LOGIN_SMS, deviceId}, "_");
        String cacheCheckCode = stringRedisTemplate.opsForValue().get(checkCodeKey);
        if (StringUtils.isEmpty(cacheCheckCode)) {
            throw new BizException("验证码已失效，请重新获取",false);
        }

        if (!StringUtils.equals(cacheCheckCode, imgCode)) {
            throw new BizException("验证码错误，请重新输入",false);
        }
        User user = this.findByAccountNo( accountNo);
        if (user == null) {
            throw new BizException("账号不存在",false);
        }
        if (user.getStatus() == UserStatusEnum.INIT.getCode()) {
            throw new BizException("账号待审核", false);
        }
        if (user.getStatus() == UserStatusEnum.NOT_PASS.getCode()) {
            throw new BizException("账号不可用", false);
        }
        String newPwd = MD5Utils.MD5EncodeForPwd(UserConstants.PWD_KEY, pwd.toUpperCase());
        if (!user.getPwd().equalsIgnoreCase(newPwd)) {
            throw new BizException("密码不正确",false);
        }
        return user;
    }


    public User loginBySmsCode( String areaCode, String mobile, String smsCode) {
        User dbUser = this.findByMobile(areaCode, mobile);
        if (dbUser == null) {
            throw new BizException("用户不存在");
        }

        //短信验证码是否正确
        String checkCodeKey = StringUtils.join(new Object[]{CacheConstants.USER_LOGIN_SMS, areaCode + mobile}, "_");

        String cacheCheckCode = stringRedisTemplate.opsForValue().get(checkCodeKey);
        if (StringUtils.isEmpty(cacheCheckCode)) {
            throw new BizException("验证码已失效，请重新获取");
        }

        if (!StringUtils.equals(cacheCheckCode, smsCode)) {
            throw new BizException("验证码错误，请重新输入");
        }
        return dbUser;
    }


    public User loginByEmailCode( String email, String emailCode) {
        User dbUser = this.findByEmail(email);
        if (dbUser == null) {
            throw new BizException("用户不存在");
        }

        //短信验证码是否正确
        String checkCodeKey = StringUtils.join(new Object[]{CacheConstants.USER_LOGIN_SMS, email}, "_");

        String cacheCheckCode = stringRedisTemplate.opsForValue().get(checkCodeKey);
        if (StringUtils.isEmpty(cacheCheckCode)) {
            throw new BizException("验证码已失效，请重新获取");
        }

        if (!StringUtils.equals(cacheCheckCode, emailCode)) {
            throw new BizException("验证码错误，请重新输入");
        }
        return dbUser;
    }

    @Transactional
    public User loginByBind(UserLoginByBindParam param) {
        String openId = param.getOpenId();

        OuterUser outerUser = outerUserService.findByTypeAndOpenId(param.getBindType(), openId);

        if (outerUser == null) {
            synchronized (openId) {
                //初始化
                outerUser = new OuterUser();
                outerUser.setOpenId(openId);
                outerUser.setAvatar(param.getAvatar());
                outerUser.setBindType(param.getBindType());
                outerUser.setNickname(param.getNickname());
                outerUser.setStatus(YesNoEnum.YES.getCode());

                //用户初始化
                User user = new User();
                //是否默认绑定租户
                String autoBindTenant = dataConfigService.findValueByCode(DataConfigConstant.AUTO_BIND_TENANT);
                if (BooleanUtil.toBoolean(autoBindTenant)) {
                    String autoBindTenantId = dataConfigService.findValueByCode(DataConfigConstant.AUTO_BIND_TENANT_ID);
                    user.setTenantId(NumberUtil.getNumber(autoBindTenantId));
                    user.setBindTenant(YesNoEnum.YES.getCode());
                }else{
                    user.setTenantId(-1L);  //表示未知
                    user.setBindTenant(YesNoEnum.NO.getCode());
                }

                String accountNo = serialNumberService.getSerialNumber("sys_user", "0");
//                String accountNo = UUID.randomUUID().toString();  //后续通过工具生成人性化的
                user.setAccountNo(accountNo);
                String defaultPwd = MD5Utils.MD5Encode("123456");
                String encodePwd = MD5Utils.MD5EncodeForPwd(UserConstants.PWD_KEY, defaultPwd);
                user.setPwd(encodePwd);
                user.setNickname(param.getNickname());
//                user.setType(UserTypeEnum.MEMBER.getCode());
                user.setStatus(UserStatusEnum.INIT.getCode());
//            user.setWechat();
//            user.setAreaCode();
//            user.setMobile();
//            user.setEmail();
                user.setGender(param.getGender());
//            user.setBirthday();
//            user.setMessage();
                user.setAvatar(param.getAvatar());
                user.setUserName(param.getNickname());
//            user.setDeptId();
//            user.setPositionId();
                user.setBind(YesNoEnum.NO.getCode());

                UserEditParam userEditParam = new UserEditParam();
                List<Long> roleIds = new ArrayList<>(); //后续放到配置表中
                roleIds.add(2L);
                userEditParam.setRoleIds(roleIds);
                this.saveUser(user,userEditParam);
//                this.saveUser(user);


                outerUser.setUserId(user.getId());
                outerUserService.save(outerUser);
            }
        }
        User user = this.findById(outerUser.getUserId());
        if (user.getStatus() == UserStatusEnum.NOT_PASS.getCode()) {
            throw new BizException("账号不可用", false);
        }
        return user;
    }


    @Transactional
    public User loginByWOACode(UserLoginForWOAParam param) {
        String checkCode = param.getCheckCode();
        CheckCodeTypeEnum checkCodeTypeEnum = CheckCodeTypeEnum.getByCode(CheckCodeTypeEnum.WOA_LOGIN.getCode());
        String cacheKey = StringUtils.join(new Object[]{checkCodeTypeEnum.getCacheKey(), checkCode}, "_");

        String openId = stringRedisTemplate.opsForValue().get(cacheKey);
        if (StringUtils.isEmpty(openId)) {
            throw new BizException("验证码已失效，请重新获取");
        }
        int bindType = OuterUserBindTypeEnum.WECHAT.getCode();
        OuterUser outerUser = outerUserService.findByTypeAndOpenId(bindType, openId);
        if (outerUser == null) {
            synchronized (openId) {
                //初始化
                outerUser = new OuterUser();
                outerUser.setOpenId(openId);
//                outerUser.setAvatar(param.getAvatar());
                outerUser.setBindType(bindType);
                 String nickname = RandomStringUtils.randomAlphabetic(10);
                outerUser.setNickname(nickname);
                outerUser.setStatus(YesNoEnum.YES.getCode());

                //用户初始化
                User user = new User();
                //是否默认绑定租户
                String autoBindTenant = dataConfigService.findValueByCode(DataConfigConstant.AUTO_BIND_TENANT);
                if (BooleanUtil.toBoolean(autoBindTenant)) {
                    String autoBindTenantId = dataConfigService.findValueByCode(DataConfigConstant.AUTO_BIND_TENANT_ID);
                    user.setTenantId(NumberUtil.getNumber(autoBindTenantId));
                    user.setBindTenant(YesNoEnum.YES.getCode());
                }else{
                    user.setTenantId(-1L);  //表示未知
                    user.setBindTenant(YesNoEnum.NO.getCode());
                }

                String accountNo = serialNumberService.getSerialNumber("sys_user", "0");
//                String accountNo = UUID.randomUUID().toString();  //后续通过工具生成人性化的
                user.setAccountNo(accountNo);
                String defaultPwd = MD5Utils.MD5Encode("123456");
                String encodePwd = MD5Utils.MD5EncodeForPwd(UserConstants.PWD_KEY, defaultPwd);
                user.setPwd(encodePwd);
                user.setNickname(nickname);
//                user.setType(UserTypeEnum.MEMBER.getCode());
                String userAutoAuditPass = dataConfigService.findValueByCode(DataConfigConstant.USER_AUTO_AUDIT_PASS);
                if (BooleanUtil.toBoolean(userAutoAuditPass)) {
                    user.setStatus(UserStatusEnum.PASS.getCode());
                }else{
                    user.setStatus(UserStatusEnum.INIT.getCode());
                }
                user.setLevel(UserLevelEnum.NORMAL.getCode());
                String probationDays = dataConfigService.findValueByCode(DataConfigConstant.PROBATION_DAYS);
                Date gmtExpire = DateUtils.addDays(new Date(), Integer.valueOf(probationDays));
                user.setGmtExpire(gmtExpire);



//            user.setWechat();
//            user.setAreaCode();
//            user.setMobile();
//            user.setEmail();
                user.setGender(UserGenderEnum.MALE.getCode());
//            user.setBirthday();
//            user.setMessage();
//                user.setAvatar(param.getAvatar());
                user.setUserName(nickname);
//            user.setDeptId();
//            user.setPositionId();
                user.setBind(YesNoEnum.NO.getCode());

                UserEditParam userEditParam = new UserEditParam();
                RoleQuery roleQuery = new RoleQuery();
                roleQuery.setNormal(YesNoEnum.YES.getCode());
                List<Role> roleList = roleService.findAll(roleQuery);
                List<Long> roleIds = roleList.stream().map(data -> data.getId()).collect(Collectors.toList());
//                roleIds.add(2L);  //暂时写死
                userEditParam.setRoleIds(roleIds);
                this.saveUser(user,userEditParam);
//                this.saveUser(user);


                outerUser.setUserId(user.getId());
                outerUserService.save(outerUser);
            }
        }
        User user = this.findById(outerUser.getUserId());
        if (user.getStatus() == UserStatusEnum.NOT_PASS.getCode()) {
            throw new BizException("账号不可用", false);
        }
        return user;
    }

    public User bindTenant(Long userId, Long tenantId) {
        synchronized (userId) {
            TenantUserQuery tenantUserQuery = new TenantUserQuery();
            tenantUserQuery.setUserId(userId);
            tenantUserQuery.setTenantId(tenantId);
            TenantUser tenantUser = tenantUserService.findOne(tenantUserQuery);
            if (tenantUser == null) {
                tenantUser = new TenantUser();
                tenantUser.setUserId(userId);
                tenantUser.setTenantId(tenantId);
                tenantUser.setStatus(UserStatusEnum.INIT.getCode());
                tenantUserService.save(tenantUser);
            }
            //修改用户状态
            User user = this.findById(userId);
            user.setTenantId(tenantId);
            user.setBindTenant(YesNoEnum.YES.getCode());
            user.setStatus(tenantUser.getStatus());
            this.save(user);
            return user;
        }
    }

    public String getOpenId(UserLoginForOpenIdParam param) {
        String code = param.getCode();
        String appId = "wx63bcffdc11c6b0b0";
        String secret = "16eb88fcdff0d3c3005dfa0d9d3aea1f";
        if (param.getBindType() == OuterUserBindTypeEnum.WECHAT.getCode()) {  //小程序
            String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code";
            ResponseRes responseRes = HttpNewUtils.get(url);
            HashMap<String, Object> resultMap = JsonUtil.json2ObjectMap(responseRes.getBodyText());
            if (!resultMap.containsKey("openid")) {
                throw new BizException("获取openId失败");
            }
            return resultMap.get("openid").toString();
        }
        throw new BizException("获取openId失败");
    }

    public User getUserByOpenId(Integer bindType, String openId) {
        OuterUser outUser = this.outerUserService.findByTypeAndOpenId(bindType, openId);
        if (outUser != null) {
            return this.findById(outUser.getUserId());
        } else {
            return null;
        }
    }



    public LoginResultVo getLoginResultVoByUserId(Long userId){
        User user = this.findById(userId);
        String token = this.resetLoginToken(user);
        LoginResultVo loginResultVo = new LoginResultVo();
        loginResultVo.setToken(token);
        UserVo userVo = BeanUtils.beanCopy(user, UserVo.class);
        loginResultVo.setUserInfo(userVo);
        return loginResultVo;
    }

    public void del(List<Long> ids){
        //删除，1如果已经产生业务记录，不能删除，只有第一次未审核才能进行删除
        for (Long id : ids) {
            User user = this.findById(id);
            if(user.getStatus() != UserStatusEnum.INIT.getCode()){
                throw new BizException("删除用户中包含已审核或审核过的用户，不允许删除");
            }
            //先删除角色
            this.getBaseMapper().deleteUserRoleByUserId(id);
        }
        this.deletePhysByIds(ids);
    }

//    public static void main(String[] args) {
//        long l = TimeUnit.DAYS.toSeconds(30L);
//        System.out.println(l);
//    }

}
